﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Timers;
using Rabbit.Model;
using System.Collections.ObjectModel;
using Rabbit.Utils;

namespace Rabbit.BusinessLogicLayer
{
    public abstract class AutoUpdateMessageClassBase<T>
    {
        public DateTime LastUpdateTime { get; set; }
        protected long? LastMaxID = null;
        public Action<List<T>> OnNewMessageReceived;
        public Action<List<T>> OnMessageInitialized;
        public Action<MaxRatesExceededException> OnMaxRateExceededExceptionOccured;
        public Action<PublishAPICallTooFrequentException> OnPublishAPICallTooFrequent;
        public Action<Exception> OnUnknownExceptionOccured;
        public ObservableCollection<Status> MainMessageList;

        protected bool IsInitialized = false;
        protected System.Timers.Timer timer = new System.Timers.Timer();

        private double UpdateInterval;

        protected Stack<long> CursorQueue = new Stack<long>();
        protected long NextPageCursorID = 0;
        protected long? CurrentPageCursorID;

        public AutoUpdateMessageClassBase(double AutoUpdateMessageInterval)
        {
            UpdateInterval = AutoUpdateMessageInterval;
            timer.Interval = UpdateInterval * 1000;
            timer.AutoReset = true;
            timer.Elapsed += new ElapsedEventHandler(RefreshMessageTimer_Elapsed);
        }

        /// <summary>
        /// start fetching message
        /// </summary>
        public void StartFetchingMessage()
        {
            Thread thread = new Thread(new ThreadStart(() =>
            {
                RefreshMessageTimer_Elapsed(null, null);
                timer.Start();
            }));
            thread.Start();
        }

        public virtual bool CanGetPreviousePage
        {
            get
            {
                return CurrentPageCursorID != -1;
            }
        }
        /// <summary>
        /// reset the timer interval and restart the timer
        /// </summary>
        /// <param name="AutoUpdateMessageInterval"></param>
        public void ResetAutoUpadteMessageInterval(double AutoUpdateMessageInterval)
        {
            UpdateInterval = AutoUpdateMessageInterval;
            timer.Stop();
            timer.Interval = UpdateInterval * 1000;
            timer.Start();
        }

        //decrease auto update message interval to refresh the message more quickly
        internal void ResetTimerIntervalToUserConfigured()
        {
            timer.Stop();
            timer.Interval = UpdateInterval * 1000;
            timer.Start(); 
        }

        //increase auto update message interval to refresh the message more slowly
        public void IncreaseAutoUpadteMessageInterval()
        {
            if (timer.Interval <= Configuration.MaxMessageUpdateInterval*1000)
            {
                timer.Stop();
                timer.Interval += 10 * 1000;
                timer.Start();
            }
        }
        /// <summary>
        /// the the timer elapsed method
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void RefreshMessageTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                LastUpdateTime = DateTime.Now;
                if (sender == null)
                {
                    FetchMessage();
                }
                else
                {
                    FetchNewMessage();
                }
            }
            catch (PublishAPICallTooFrequentException ex)
            {
                RaisePublishAPICallTooFrequentExceptionEvent(ex);
            }
            catch (MaxRatesExceededException ex)
            {
                RaiseMaxRateExceededExceptionEvent(ex);
            }
            catch (Exception ex)
            {
                RaiseUnknownExceptionOccuredEvent(ex);
            }
        }

        /// <summary>
        /// remember to call RaiseNewMessageReceivedEvent in this method
        /// </summary>
        protected abstract void FetchNewMessage();
        protected abstract void FetchMessage();
        public abstract List<T> GetNextPageMessage();
        public abstract List<T> GetPreviousePageMessage();
        public abstract List<T> GetFirstPageMessage();

        protected void RaiseNewMessageReceivedEvent(List<T> messageList)
        {
            if (IsInitialized == false)
            {
                RaiseMessageFetchedEvent(messageList);
            }
            else
            {
                if (OnNewMessageReceived == null)
                {
                    throw new InvalidOperationException("You haven't assigned OnMessageReceived<T> delegate, please assigned it before calling this method");
                }

                //if new message received, reset timer interval to user configured
                //else, increase the interval by 10 seconds to decrease the api call frequency
                if (messageList.Count>0)
                {
                    ResetTimerIntervalToUserConfigured();
                }
                else
                {
                    IncreaseAutoUpadteMessageInterval();
                }

                OnNewMessageReceived(messageList);
            }
        }

        protected void RaiseMessageFetchedEvent(List<T> messageList)
        {
            if (OnMessageInitialized == null)
            {
                throw new InvalidOperationException("You haven't assigned OnMessageReceived<T> delegate, please assigned it before calling this method");
            }
            else
            {
                IsInitialized = messageList.Count > 0;
                //raise the delegate to notify the caller new message has been received
                OnMessageInitialized(messageList);
            }
        }

        protected void RaiseMaxRateExceededExceptionEvent(MaxRatesExceededException ex)
        {
            if (OnMaxRateExceededExceptionOccured != null)
            {
                OnMaxRateExceededExceptionOccured(ex);
            }
        }

        protected void RaisePublishAPICallTooFrequentExceptionEvent(PublishAPICallTooFrequentException ex)
        {
            if (OnPublishAPICallTooFrequent != null)
            {
                OnPublishAPICallTooFrequent(ex);
            }
        }
        protected void RaiseUnknownExceptionOccuredEvent(Exception ex)
        {
            if (OnMaxRateExceededExceptionOccured != null)
            {
                OnUnknownExceptionOccured(ex);
            }
        }
    }
}
